//---------------------------------------------------------------------------
#include <vcl.h>
#include <map.h>
#pragma hdrstop

#include "CRib.h"
#include "eic.h"
#pragma package(smart_init)

#define MODULE_TAG 2002
#define DEF_TOP  13

struct sConversionCaractereEnChaine
{
  char caractere;
  char* chaine;
};

const sConversionCaractereEnChaine TabConversion[]=
{
  {'0',"0"},
  {'1',"1"},
  {'2',"2"},
  {'3',"3"},
  {'4',"4"},
  {'5',"5"},
  {'6',"6"},
  {'7',"7"},
  {'8',"8"},
  {'9',"9"},
  {'a',"10"},
  {'b',"11"},
  {'c',"12"},
  {'d',"13"},
  {'e',"14"},
  {'f',"15"},
  {'g',"16"},
  {'h',"17"},
  {'i',"18"},
  {'j',"19"},
  {'k',"20"},
  {'l',"21"},
  {'m',"22"},
  {'n',"23"},
  {'o',"24"},
  {'p',"25"},
  {'q',"26"},
  {'r',"27"},
  {'s',"28"},
  {'t',"29"},
  {'u',"30"},
  {'v',"31"},
  {'w',"32"},
  {'x',"33"},
  {'y',"34"},
  {'z',"35"},
};
//---------------------------------------------------------------------------
// ValidCtrCheck est utilisé pour être sûr que les composants créés ne
// contiennent aucune fonction virtuelle pure.
//
static inline void ValidCtrCheck(CRib *)
{
    new CRib(NULL);
}
//---------------------------------------------------------------------------
extern "C" int __export _cdecl IsRIBValid( char *szBanque, char *szGuichet, char *szCompte, char *szCle)
{
  int nResult = 0;
  try {
    CRib *rib    = new CRib( NULL);
    rib->Banque  = szBanque;
    rib->Guichet = szGuichet;
    rib->Compte  = szCompte;
    rib->Rib     = szCle;
    nResult      = rib->IsValide;
    delete rib;
  } catch( Exception &e) {
  }
  return nResult;
}
//---------------------------------------------------------------------------
__fastcall CRib::CRib(TComponent* Owner)
    : TCustomPanel(Owner)
{
  BorderStyle     = bsNone;
  BevelInner      = bvNone;
  BevelOuter      = bvNone;
  m_nLeft         = 0;
  m_nTop          = DEF_TOP;
  m_bReadOnly     = false;
  m_bEnabled      = true;
  m_MessageErreur = true;
  m_bDeuxLignes   = false;

  // Initialisation des composants internes
  InitComposantInterne( &m_pEDesignation, &m_pLDesignation, LONGUEUR_DESIGNATION_RIB, "Désignation", CEdit::FiltresEdit() << CEdit::feAlpha << CEdit::feNumerique << CEdit::feBlanc);
  InitComposantInterne( &m_pEBanque     , &m_pLBanque     , LONGUEUR_BANQUE_RIB     , "Banque"     , CEdit::FiltresEdit() << CEdit::feNumerique);
  InitComposantInterne( &m_pEGuichet    , &m_pLGuichet    , LONGUEUR_GUICHET_RIB    , "Guichet"    , CEdit::FiltresEdit() << CEdit::feNumerique);
  InitComposantInterne( &m_pENumCpt     , &m_pLNumCpt     , LONGUEUR_COMPTE_RIB     , "N° compte"  , CEdit::FiltresEdit() << CEdit::feAlpha << CEdit::feNumerique);
  InitComposantInterne( &m_pERIB        , &m_pLRIB        , LONGUEUR_CLE_RIB        , "Clé"        , CEdit::FiltresEdit() << CEdit::feNumerique);

  SetDeuxLignes(m_bDeuxLignes);
}
//---------------------------------------------------------------------------
void __fastcall CRib::InitComposantInterne( CEdit **pIntEdit, TLabel **pIntLabel, int nMaxChar, const AnsiString &strLabel, CEdit::FiltresEdit eFiltre)
{
  CEdit *pEdit      = *pIntEdit = new CEdit( this);
  pEdit->Left       = m_nLeft;
  pEdit->Width      = 8 * nMaxChar + 4;
  pEdit->Top        = DEF_TOP;
  pEdit->MaxLength  = nMaxChar;
  pEdit->CharCase   = ecUpperCase;
  pEdit->Parent     = this;
  pEdit->Filtres    = eFiltre;

  m_nLeft          += pEdit->Width;

  TLabel *pLabel    = *pIntLabel = new TLabel( this);
  pLabel->Alignment = taCenter;
  pLabel->AutoSize  = false;
  pLabel->Left      = pEdit->Left;
  pLabel->Width     = pEdit->Width;
  pLabel->Top       = pEdit->Top-DEF_TOP;
  pLabel->Caption   = strLabel;
  pLabel->Parent    = this;
}
//---------------------------------------------------------------------------
AnsiString __fastcall CRib::GetDomiciliation() const
{
  return m_pEDesignation->Text + "#" +
              m_pEBanque->Text + "#" +
             m_pEGuichet->Text + "#" +
              m_pENumCpt->Text + "#" +
                 m_pERIB->Text;
}
//---------------------------------------------------------------------------
void __fastcall CRib::SetDomiciliation( const AnsiString &strValue)
{
  AnsiString strVal( strValue);
  int        nPos;

  if ( strVal.IsEmpty()) {
    Designation = EmptyStr;
    Banque      = EmptyStr;
    Guichet     = EmptyStr;
    Compte      = EmptyStr;
    Rib         = EmptyStr;
    return;
  }

  if(( nPos=strVal.Pos( "#"))==0) {
    Designation = strVal;
    Banque      = EmptyStr;
    Guichet     = EmptyStr;
    Compte      = EmptyStr;
    Rib         = EmptyStr;
    return;
  } else {
    Designation = strVal.SubString(      1, nPos-1);
    strVal      = strVal.SubString( nPos+1, strVal.Length()-nPos);
  }

  nPos        = strVal.Pos( "#"); ASSERT( nPos);
  Banque      = strVal.SubString(      1, nPos-1);
  strVal      = strVal.SubString( nPos+1, strVal.Length()-nPos);

  nPos        = strVal.Pos( "#"); ASSERT( nPos);
  Guichet     = strVal.SubString(      1, nPos-1);
  strVal      = strVal.SubString( nPos+1, strVal.Length()-nPos);

  nPos        = strVal.Pos( "#"); ASSERT( nPos);
  Compte      = strVal.SubString(      1, nPos-1);
  strVal      = strVal.SubString( nPos+1, strVal.Length()-nPos);

  Rib         = strVal;
}
//---------------------------------------------------------------------------
AnsiString __fastcall CRib::ConvAlpha2Numeric( const AnsiString &strValue) const
{
  char       c;
  AnsiString strValConv( strValue);

  AnsiString strLookup( "ABCDEFGHI JKLMNOPQR  STUVWXYZ");
  for( int i=1; i<=strValConv.Length(); i++)
    if( !isdigit( strValConv[i])) strValConv[i] = ( strLookup.Pos( AnsiString( strValConv[i])) % 10) + '0';
  return AnsiString::StringOfChar( '0', 11-strValConv.Length()) + strValConv;
}
//---------------------------------------------------------------------------
typedef std::map<char,AnsiString> MapConversionCaractereEnChaine;
AnsiString __fastcall ConversionNumeriqueIban(const AnsiString &pIBAN)
{
  AnsiString resultat;
  MapConversionCaractereEnChaine mapConversionCaractereEnChaine;
  MapConversionCaractereEnChaine::iterator it;
  for (int i = 0; i < ARRAYSIZE(TabConversion);i++)
    mapConversionCaractereEnChaine.insert(MapConversionCaractereEnChaine::value_type(TabConversion[i].caractere,TabConversion[i].chaine));
  for(int j = 1; j <= pIBAN.Length();j++)
  {
    it = mapConversionCaractereEnChaine.find(pIBAN[j]);
    if (it != mapConversionCaractereEnChaine.end())
      resultat += it->second;
  }
  return resultat;
}
//---------------------------------------------------------------------------
AnsiString  __fastcall CalculerIBAN(const AnsiString &pBanque, const AnsiString &pGuichet, const AnsiString &pCompte, const AnsiString &pCle)
{
  AnsiString iban;
  int ibanKey = 0;
  try
  {
    iban = pBanque+ pGuichet + pCompte + pCle + "FR00";
    iban = ConversionNumeriqueIban(iban.LowerCase());
    for (int i = 1 ; i <= iban.Length() ; i++)
      ibanKey = (ibanKey * 10 + (iban[i] - '0')) % 97;
    ibanKey = 98 - ibanKey;
    iban = AnsiString("FR") + AnsiString(ibanKey) + pBanque+ pGuichet + pCompte + pCle;
  }
  catch (Exception &e)
  {
    EICDisplayException(TAGL,e);
  }
  return iban;
}
//---------------------------------------------------------------------------

#ifdef _DEBUG
int __fastcall VerifRIBMethod2( const AnsiString &strBanque, const AnsiString &strGuichet, const AnsiString &strCompte)
{
  __int64 a = strBanque.ToInt();
  __int64 b = strGuichet.ToInt();
  __int64 c = _atoi64( strCompte.c_str());

  __int64 d = (a * 8);
          a = d / 97;
          a = d - (a * 97);

  __int64 e = (b * 15);
          b = e / 97;
          b = 97 - (e - (b * 97));

  __int64 f = (c * 3);
          c = f / 97;
          c = 97 - (f - (c * 97));

  __int64 g = a + b + c;
  __int64 h = g / 97;
  __int64 i = g - (h * 97);

  if( i==0) i = 97;

  return i;
}
#endif
//---------------------------------------------------------------------------
int __fastcall CRib::CalculRIB() const
{
  AnsiString strValue( m_pEBanque->Text + m_pEGuichet->Text + ConvAlpha2Numeric( m_pENumCpt->Text));

  int na = strValue.SubString(  1, 7).ToInt();
  int nb = strValue.SubString(  8, 7).ToInt();
  int nc = strValue.SubString( 15, 7).ToInt();
  int nRIB = 97 - ( 62*na + 34*nb + 3*nc) % 97;
#ifdef _DEBUG
  if( strValue.Length()==21)
    ASSERT( VerifRIBMethod2( m_pEBanque->Text, m_pEGuichet->Text, ConvAlpha2Numeric( m_pENumCpt->Text))==nRIB);
#endif
  return nRIB;
}
//---------------------------------------------------------------------------
void __fastcall CRib::DoExit()
{
  if( !m_pEBanque->Text.IsEmpty() || !m_pEGuichet->Text.IsEmpty() || !m_pENumCpt->Text.IsEmpty() || !m_pERIB->Text.IsEmpty())
  {
    if( !IsValide && AfficherMessageErreurEnAutomatique)
    {
      AnsiString strRIB;
      #ifdef _DEBUG
      try { strRIB = " (" + IntToStr( CalculRIB()) + ")"; } catch( ...) { }
      #endif
        if( ::EICQuestion( "Le numéro du compte bancaire est invalide.\nVoulez-vous modifier votre saisie ?" + strRIB, "Erreur..."))
        {
          m_pEDesignation->SetFocus();
          return;
        }
        else Domiciliation = EmptyStr;
    }
  }
  TCustomPanel::DoExit();
}
//---------------------------------------------------------------------------
bool __fastcall CRib::GetIsValide() const
{
  // Vérifie que les champs composants le RIB ont bien été saisis
  bool bRibOk = false;
  try
    {
      bRibOk  = (  m_pEBanque->Text.Length()== m_pEBanque->MaxLength) &&
                ( m_pEGuichet->Text.Length()==m_pEGuichet->MaxLength) &&
                (     m_pERIB->Text.Length()==    m_pERIB->MaxLength) &&
                 !m_pENumCpt->Text.IsEmpty() &&
                ( m_pERIB->Text.ToInt()==CalculRIB()); // Vérification de la clé RIB

      if( bRibOk && ( m_pENumCpt->Text.Length()!=m_pENumCpt->MaxLength))
          m_pENumCpt->Text = AnsiString::StringOfChar( '0', m_pENumCpt->MaxLength-m_pENumCpt->Text.Length()) + m_pENumCpt->Text;
    }
  catch (...)
    {
    }
  return bRibOk;
}
//---------------------------------------------------------------------------
bool __fastcall CRib::GetIsEmpty() const
{
  return m_pEDesignation->Text.IsEmpty() &&
         m_pEBanque->Text.IsEmpty() &&
         m_pEGuichet->Text.IsEmpty() &&
         m_pENumCpt->Text.IsEmpty() &&
         m_pERIB->Text.IsEmpty();
}
//---------------------------------------------------------------------------
void __fastcall CRib::SetReadOnly( bool bValue)
{
  if( bValue==m_bReadOnly) return;

  m_bReadOnly = bValue;

  m_pEDesignation->ReadOnly = m_bReadOnly;
       m_pEBanque->ReadOnly = m_bReadOnly;
      m_pEGuichet->ReadOnly = m_bReadOnly;
       m_pENumCpt->ReadOnly = m_bReadOnly;
          m_pERIB->ReadOnly = m_bReadOnly;
}
//---------------------------------------------------------------------------
void __fastcall CRib::SetEnabled( bool bValue)
{
  if( bValue==m_bEnabled) return;

  m_bEnabled = bValue;

  for( int i=ControlCount-1; i>=0; i--) {
    TControl *pCtrl = Controls[i];
    if( dynamic_cast<TLabel*>( pCtrl) || dynamic_cast<CEdit*>( pCtrl))
        pCtrl->Enabled = m_bEnabled;
  }
}
//---------------------------------------------------------------------------
void __fastcall CRib::SetFocus(void)
{
  m_pEDesignation->SetFocus();
}
//---------------------------------------------------------------------------
void __fastcall CRib::SetDeuxLignes(bool bValue)
{
try {
    m_bDeuxLignes = bValue;

    m_nLeft = 0;
    m_nTop = DEF_TOP;

    // Si deux lignes la largeur est égale à celle de la désignation
    // sinon c'est la largeur totale des composants
    if (m_bDeuxLignes) {
        Width   = m_pEDesignation->Width + 1;
    } else {
        Width   = m_pEDesignation->Width
                + m_pEBanque->Width
                + m_pEGuichet->Width
                + m_pENumCpt->Width
                + m_pERIB->Width
                + 1;
    }

    // Déplace les composants en fonction de la largeur
    ReplaceComposantInterne( m_pEDesignation    , m_pLDesignation   );
    ReplaceComposantInterne( m_pEBanque         , m_pLBanque        );
    ReplaceComposantInterne( m_pEGuichet        , m_pLGuichet       );
    ReplaceComposantInterne( m_pENumCpt         , m_pLNumCpt        );
    ReplaceComposantInterne( m_pERIB            , m_pLRIB           );

    // La hauteur est fonction du placement des composants
    Height = m_nTop + m_pLRIB->Height + 5;

    } catch (Exception &e) {
        EICDisplayException(TAGL,e);
    }
}
//---------------------------------------------------------------------------
void __fastcall CRib::ReplaceComposantInterne( CEdit *pIntEdit, TLabel *pIntLabel)
{
try {
    if ((m_nLeft != 0) && (m_nLeft + pIntEdit->Width) > Width) {
        m_nLeft = 0;
        m_nTop  += DEF_TOP + pIntEdit->Height;
    }
    pIntEdit->Left      = m_nLeft;
    pIntEdit->Top       = m_nTop;
    pIntLabel->Left     = m_nLeft;
    pIntLabel->Top      = m_nTop - DEF_TOP;

    m_nLeft += pIntEdit->Width;

    } catch (Exception &e) {
        EICDisplayException(TAGL,e);
    }
}
//---------------------------------------------------------------------------
namespace Crib
{
    void __fastcall PACKAGE Register()
    {
        TComponentClass classes[1] = {__classid(CRib)};
        RegisterComponents("EIC Ctrlstd", classes, 0);
    }
}
//---------------------------------------------------------------------------